/**
 * 
 */
package org.tp8.ektelesis.listener;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;
import org.tp8.ektelesis.exc.KeyHookException;

/**
 * @author tpana
 * 
 */
public class KeyCombinationListener implements NativeKeyListener {

	private final static Logger LOGGER = Logger
			.getLogger(KeyCombinationListener.class.toString());

	private Map<Integer, Boolean> pressedKeys;

	private CombinationAction action;

	/**
	 * Constructor
	 */
	public KeyCombinationListener(int[] keyCodes, CombinationAction action) {
		// Initialize keys
		pressedKeys = new HashMap<Integer, Boolean>();
		for (int i : keyCodes) {
			pressedKeys.put(i, false);
		}
		// Apply action
		this.action = action;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.jnativehook.keyboard.NativeKeyListener#nativeKeyPressed(org.jnativehook
	 * .keyboard.NativeKeyEvent)
	 */
	@Override
	public void nativeKeyPressed(NativeKeyEvent event) {
		updateKeyStatus(event.getKeyCode(), true);
		try {
			checkCombination();
		} catch (KeyHookException e) {
			LOGGER.throwing("nativeKeyPressed", "error", e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.jnativehook.keyboard.NativeKeyListener#nativeKeyReleased(org.jnativehook
	 * .keyboard.NativeKeyEvent)
	 */
	@Override
	public void nativeKeyReleased(NativeKeyEvent event) {
		updateKeyStatus(event.getKeyCode(), false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.jnativehook.keyboard.NativeKeyListener#nativeKeyTyped(org.jnativehook
	 * .keyboard.NativeKeyEvent)
	 */
	@Override
	public void nativeKeyTyped(NativeKeyEvent event) {
		// Do nothing.
	}

	/**
	 * Getter
	 * 
	 * @return Map<Integer, Boolean>
	 */
	public Integer[] getPressedKeys() {
		Set<Integer> keys = pressedKeys.keySet();
		Integer[] intArray = new Integer[keys.size()];
		return keys.toArray(intArray);
	}

	/**
	 * Updates the keys currently pressed.
	 * 
	 * @param keyCode
	 *            The key code.
	 * @param value
	 *            The value to apply.
	 */
	private void updateKeyStatus(int keyCode, boolean value) {
		if (pressedKeys.containsKey(keyCode)) {
			pressedKeys.put(keyCode, value);
		}
	}

	/**
	 * Checks the combination and if met, fires the action.
	 * 
	 * @throws KeyHookException
	 *             in case an error is raised while firing the action.
	 */
	private void checkCombination() throws KeyHookException {
		boolean shouldExecute = true;
		for (Map.Entry<Integer, Boolean> entry : pressedKeys.entrySet()) {
			shouldExecute &= entry.getValue();
		}
		if (shouldExecute) {
			action.execute();
		}
	}

}
